package com.fuyun.rpc.client.fault.tolerant;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.fuyun.rpc.client.common.RpcTcpService;
import com.fuyun.rpc.client.config.RpcApplication;
import com.fuyun.rpc.client.registry.Registry;
import com.fuyun.rpc.client.registry.RegistryFactory;
import com.fuyun.rpc.common.domain.RpcRequest;
import com.fuyun.rpc.common.domain.RpcResponse;
import com.fuyun.rpc.common.domain.ServiceMetaInfo;
import com.fuyun.rpc.common.serializer.Serializer;
import com.fuyun.rpc.common.serializer.SerializerFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;

/**
 * 故障转移（尝试所有节点）
 *
 * @author: zrq1351
 * @date: 2025/2/17
 */
@Slf4j
@AllArgsConstructor
public class FailOverTolerantStrategy implements TolerantStrategy {
	/**
	 * 在出现异常时执行容错逻辑，尝试通过其他服务节点重新发送请求 此方法首先从上下文中获取必要的请求信息和服务信息，然后尝试通过其他服务节点重新发送请求 如果所有节点都不可用，则抛出运行时异常
	 *
	 * @param context 包含请求和服务信息的上下文映射
	 * @param e       引发容错逻辑的异常
	 * @return 返回重新发送请求的响应
	 * @throws RuntimeException 如果所有服务节点都不可用，则抛出此异常
	 */
	@Override
	public RpcResponse doTolerant(Map<String, Object> context, Exception e) {
		RpcRequest rpcRequest = (RpcRequest)context.get("rpcRequest");
		RpcApplication rpcApplication = (RpcApplication)context.get("rpcApplication");
		ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
		serviceMetaInfo.setServiceName(rpcRequest.getServiceName());
		serviceMetaInfo.setServiceVersion(rpcApplication.getRegistryConfig().getServiceVersion());
		serviceMetaInfo.setServiceGroup(rpcApplication.getRegistryConfig().getGroup());
		Registry registry = RegistryFactory.getInstance(rpcApplication.getRpcConfig().getRegistry());
		List<ServiceMetaInfo> nodes = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
		Serializer serializer = SerializerFactory.getInstance(rpcApplication.getRpcConfig().getSerializer());
		byte[] bytes = serializer.serialize(rpcRequest);
		for (ServiceMetaInfo node : nodes) {
			String serviceAddress = node.getServiceAddress();
			try {
				String protocolName = rpcApplication.getRpcConfig().getProtocolName();
				if ("http".equals(protocolName)) {
					return sendHttpRequest(serviceAddress, bytes, rpcApplication);
				} else if ("tcp".equals(protocolName)) {
					return sendTcpRequest(node, bytes, rpcApplication);
				}
			} catch (Exception ex) {
				log.error("调用远程服务失败: {}:{}", serviceAddress, rpcRequest.getMethodName(), ex);
			}
		}
		throw new RuntimeException("所有服务节点都不可用");
	}

	private RpcResponse sendHttpRequest(String serviceAddress, byte[] bytes, RpcApplication rpcApplication) {
		try (HttpResponse httpResponse = HttpRequest.post(serviceAddress).body(bytes).execute()) {
			byte[] bodyBytes = httpResponse.bodyBytes();
			Serializer serializer = SerializerFactory.getInstance(rpcApplication.getRpcConfig().getSerializer());
			return serializer.deserialize(bodyBytes, RpcResponse.class);
		}
	}

	private RpcResponse sendTcpRequest(ServiceMetaInfo node, byte[] bytes, RpcApplication rpcApplication) {
		return RpcTcpService.getRpcResponse(node, bytes, rpcApplication);
	}

}